#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "MyI2C.h"

#define SCL_PIN GPIO_Pin_10
#define SDA_PIN GPIO_Pin_11

u8 temp;
u8 ucErrTime;
/**
* I2C基本通信协议：
*	起始条件：SCL高电平期间， SDA从高电平切换为低电平

*	终止条件：SCL高电平期间， SDA从低电平切换为高电平

*	主机向从机发送数据：
*			1、发送一个字节：SCL低电平期间，主机将数据位依次放到SDA线上（高位先行），
*				然后释放SCL，从机将在SCL高电平期间读取数据位，所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可发送一个字节

* 主机接收从机发送数据:
*			1、接收一个字节：SCL低电平期间，从机将数据位依次放到SDA线上（高位先行），然后释放SCL，主机将在SCL高电平期间读取数据位，
*				所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可接收一个字节（主机在接收之前，需要释放SDA）

*	主机向从机应答：
            发送应答：主机在接收完一个字节之后，在下一个时钟发送一位数据，数据0表示应答，数据1表示非应答

* 主机接收从机应答：
            接收应答：主机在发送完一个字节之后，在下一个时钟接收一位数据，判断从机是否应答，数据0表示应答，数据1表示非应答（主机在接收之前，需要释放SDA）

*
*
*
*/
void MyI2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
    GPIO_InitStructure.GPIO_Pin = SCL_PIN | SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    // 置高电平，初始化状态
    GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}
void MyI2C_SDA_In(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    //// 置高电平，初始化状态
    // GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}
void MyI2C_SCL_In(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    //// 置高电平，初始化状态
    // GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}
void MyI2C_SDA_OUT(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    //// 置高电平，初始化状态
    // GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}
void MyI2C_SCL_OUT(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    //// 置高电平，初始化状态
    // GPIO_SetBits(GPIOB, SCL_PIN | SDA_PIN);
}

// 设置SCL
void MyI2C_W_SCL(char Byte)
{
    GPIO_WriteBit(GPIOB, SCL_PIN, (BitAction)Byte);
    Delay_us(10); // 延时10us
}

// 读取SCL
uint8_t MyI2C_R_SCL()
{
    uint8_t Byte;
    Byte = GPIO_ReadInputDataBit(GPIOB, SCL_PIN);
    Delay_us(10); // 延时10us
    return Byte;
}

// 设置SDA
void MyI2C_W_SDA(char Byte)
{
    GPIO_WriteBit(GPIOB, SDA_PIN, (BitAction)Byte);
    Delay_us(10); // 延时10us
}

// 读取SDA
uint8_t MyI2C_R_SDA()
{
    uint8_t Byte;
    Byte = GPIO_ReadInputDataBit(GPIOB, SDA_PIN);
    Delay_us(10); // 延时10us
    return Byte;
}

/**
 * 	起始条件：SCL高电平期间， SDA从高电平切换为低电平
 */
void MyI2C_Start(void)
{
    I2C_SCL_SET;
    I2C_SDA_SET;
    IIC_NOP();
    I2C_SDA_RESET;
    IIC_NOP();
    I2C_SCL_RESET;
    IIC_NOP();
}

/**
 * 	终止条件：SCL高电平期间， SDA从低电平切换为高电平
 */
void MyI2C_Stop(void)
{
    // 先将SDA切换为高电平，避免在SCL高电平期间SDA也高电平
    ucErrTime = 0;
    I2C_SDA_OUT;
    I2C_SCL_RESET;
    I2C_SDA_RESET;
    IIC_NOP();
    I2C_SCL_IN;
    while (!I2C_SCL)
    {
        ucErrTime++;
        if (ucErrTime > 250) // 根据时钟选择合适的等待时间
        {
            // 加入合适的错误处理机制
            break;
        }
    }
    I2C_SCL_OUT;
    I2C_SCL_SET;
    IIC_NOP();
    I2C_SDA_SET;
    IIC_NOP();
}

// 主机向从机发送一个字节数据
// 1、发送一个字节：SCL低电平期间，主机将数据位依次放到SDA线上（高位先行），然后释放SCL，从机将在SCL高电平期间读取数据位，所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可发送一个字节
/**
    1010 1010
&	1000 0000
------------
    1000 0000

    1010 1010
&	0100 0000
------------
    0000 0000
*/
void MyI2C_SendByte(uint8_t Byte)
{
    ucErrTime = 0;
    I2C_SDA_OUT;
    for (temp = 0x80; temp != 0; temp = temp >> 1)
    {
        if (temp & Byte)
        {
            I2C_SDA_SET;
        }
        else
        {
            I2C_SDA_RESET;
        }
        IIC_NOP();
        I2C_SCL_IN;
        while (!I2C_SCL)
        {
            ucErrTime++;
            if (ucErrTime > 250) // 根据时钟选择合适的等待时间
            {
                // 加入合适的错误处理机制
                MyI2C_Stop();
                break;
            }
        }
        IIC_NOP();
        I2C_SCL_OUT;
        I2C_SCL_RESET;
    }
}

// 主机接收从机发送数据:
// 1、接收一个字节：SCL低电平期间，从机将数据位依次放到SDA线上（高位先行），然后释放SCL，主机将在SCL高电平期间读取数据位，所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可接收一个字节（主机在接收之前，需要释放SDA）
/**
    1010 1010
|	1000 0000
------------
    1010 1010

    1010 101 0
|	0100 0000
------------
    1110 1010
*/
uint8_t MyI2C_ReceiveByte(void)
{

    u8 data_temp;
    data_temp = 0;
    ucErrTime = 0;
    I2C_SDA_IN;
    I2C_SCL_IN;
    while (!I2C_SCL)
    {
        ucErrTime++;
        if (ucErrTime > 250) // 根据时钟选择合适的等待时间
        {
            // 加入合适的错误处理机制
            MyI2C_Stop();
            break;
        }
    }
    for (temp = 0x80; temp != 0; temp = temp >> 1)
    {
        I2C_SCL_SET;
        if (I2C_SDA == 1)
        {
            data_temp |= temp;
        }
        else
        {
        }
        IIC_NOP();
        I2C_SCL_RESET;
        I2C_SCL_OUT;
        IIC_NOP();
    }
    I2C_SDA_OUT;
    return data_temp;
}
// uint8_t MyI2C_ReceiveByte(void)
//{

//    uint8_t i = 8;
//    uint8_t ReceiveValue = 0x00; // 0b00000000
//    while (i > 0)
//    {
//        uint8_t Byte = MyI2C_R_SDA();
//        ReceiveValue = ReceiveValue | (Byte << (i - 1)); // 右移一位
//        MyI2C_W_SCL(1);                                  // 拉高电平后让从机读取
//        MyI2C_W_SCL(0);                                  // 拉低电平后 为下一次发送数据做准备
//        i--;
//    }
//    return ReceiveValue;
//}

/**

*	主机向从机应答：
            发送应答：主机在接收完一个字节之后，在下一个时钟发送一位数据，数据0表示应答，数据1表示非应答
**/
void MyI2C_SendAsk(uint8_t AckBit)
{
    I2C_SDA_OUT;
    I2C_SCL_RESET;
    // I2C_SDA = AckBit;
    if (AckBit)
        I2C_SDA_SET;
    else
        I2C_SDA_RESET;
    IIC_NOP();
    I2C_SCL_SET;
    IIC_NOP();
    I2C_SCL_RESET;
    I2C_SDA_IN;
}

/**

* 主机接收从机应答：
            接收应答：主机在发送完一个字节之后，在下一个时钟接收一位数据，判断从机是否应答，数据0表示应答，数据1表示非应答（主机在接收之前，需要释放SDA）
**/
uint8_t MyI2C_ReceiveAsk(void)
{
    ucErrTime = 0;
    I2C_SCL_RESET;
    I2C_SDA_IN;
    IIC_NOP();
    I2C_SCL_SET;
    IIC_NOP();
    while (I2C_SDA)
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            // 加入合适的错误处理机制
            MyI2C_Stop();
            return FAIL;
        }
    }
    I2C_SCL_RESET;
    I2C_SDA_OUT;
    return PASS;
}
